/** 
 * @(#)LinkRIP.java Didier Gutacker
 */

/**
 * @author Didier Gutacker
 * @version 1.1 05.09.97
 * @see Verbindung
 * @see NodeRIP
 * @see NetworkRIP
 * @see NetworkCanvasRIP
 * @see SimulationObject
 *
 */

import java.util.Vector;
import java.awt.*;

public class LinkRIP extends Verbindung {
  NodeRIP node1;
  NodeRIP node2;
  
  public LinkRIP( int id, NetworkRIP net, Graphics gc, NodeRIP node1, NodeRIP node2  ) {
    super( id, (SimulationObject) net, gc );
    this.node1 = node1;
    this.node2 = node2;
    initialized = true;
    ((NetworkRIP)net).writeln( text.linkTitleText + " " + id + " " + text.addedText );
    resetCoordinates();
    node1.connect( node2.getID(), weight, this );
    node2.connect( node1.getID(), weight, this );
  } // LinkRIP.LinkRIP

  public void finishUp() {
    ((NetworkRIP)net).writeln( text.linkTitleText + " " + id + " " + text.removedText );
    if ( ! net.isFinished() ) {
      resetCoordinates();
      dumpPackets();
    } // if
    super.finishUp();
    if ( node1 != null && ! node1.finished() ) {
      node1.disconnect( this );
    } // if
    node1 = null;
    if ( node2 != null && ! node2.finished() ) {
      node2.disconnect( this );
    } // if
    node2 = null;
    travellingPackets = null;
  } // LinkRIP.finishUp

  synchronized public void run() {
    int node1X, node2X;
    for ( ;; ) {
      paint();
      node1X = node1.getPositionX();
      node2X = node2.getPositionX();

      // set weight interaction
    
      if ( setWeightRequest ) {
	wakeUpable = false;
	String oldLabel = getLabel();
	SetWeightDialogue dialogue = new SetWeightDialogue( id, weight, text );
	dialogue.show();
	int oldWeight = weight;
	weightGuard.acquire();
	weight = dialogue.result();
	weightGuard.release();
	setWeightRequest = false;
	dialogue = null;		// go, GC, go !!! 
	marked = false;
	fontMetrics = gc.getFontMetrics();
	int textHeight = fontMetrics.getAscent() + fontMetrics.getDescent() + 1;
	gc.clearRect( textX, textY - textHeight, fontMetrics.stringWidth( oldLabel ), textHeight );
	paint();
	node1.disconnect( this );
	node2.disconnect( this );
	node1.connect( node2.getID(), weight, this );
	node2.connect( node1.getID(), weight, this );
      } // if
      
      if ( travellingPackets.isEmpty() ) {
	if ( !setWeightRequest ) {
	  if ( net.isFinished() ) {
	    return;
	  } // if
	  ((NetworkRIP)net).getCanvas().getGraphics().clipRect( xpos, ypos, width, height );
	  ((NetworkRIP)net).forcedPaint();
	  wakeUpable = true;
	  if ( paintRequested ) {
	    paintRequested = false;
	  } else {
	    try {
	      wait();
	    } catch( InterruptedException e ) {
	    } // try
	  } // if
	} // if
      } else {
	wakeUpable = false;
	try {
	  int speed = ((NetworkRIP)net).getSpeed();
	  if ( speed == 1 ) {
	    wait( 100 );
	  } else if ( speed == 0 ) {
	    wait( 300 );
	  } else {
	    wait( 10 );
	  } // if
	} catch( InterruptedException e ) {
	} // try
	// unpaint packets

	int speed = ((NetworkRIP)net).getSpeed();
	for ( int i = travellingPackets.size() - 1; i >= 0; i -= 1 ) {
	  if ( node1X < node2X ) {
	    ( (Packet) travellingPackets.elementAt( i )).unpaint( gc, x1, y1, x2, y2 );
	  } else {
	    ( (Packet) travellingPackets.elementAt( i )).unpaint( gc, x2, y2, x1, y1 );
	  } // if
	  if ( speed == 0 ) {
	    ( (Packet) travellingPackets.elementAt( i )).increaseTravelPercentage( ( 1 + 32.0 / weight ), currentTimestamp );
	  } else if ( speed == 1 ) {
	    ( (Packet) travellingPackets.elementAt( i )).increaseTravelPercentage( ( 1 + 32.0 / weight ) * 2.5, currentTimestamp );
	  } else {
	    ( (Packet) travellingPackets.elementAt( i )).increaseTravelPercentage( ( 1 + 32.0 / weight ) * 5, currentTimestamp );
	  } // if
	  if ( ( (Packet) travellingPackets.elementAt( i )).getTravelPercentage() == 0.0 ) { // on hold
	    currentTimestamp += 1;
	    currentTimestamp %= 10000;
	  } // if
	  if ( ( (Packet) travellingPackets.elementAt( i )).getTravelPercentage() == 100.0 ) {
	    processPacket( ( (Packet) travellingPackets.elementAt( i )) );
	    travellingPackets.removeElementAt( i );
	  } // if
	} // for
      } // if
      
    } // for
  } // LinkRIP.run

  synchronized public void dumpPackets() {
    for ( int i = travellingPackets.size() - 1; i >= 0; i -= 1 ) {
      processPacket( ( (Packet) travellingPackets.elementAt( i )) );
      travellingPackets.removeElementAt( i );
    } // for
  } // LinkRIP.dumpPackets

  public void paint() {
    if ( inPaint != 0 || finished ) {
      return;	// recursive invokation
    } else {
      inPaint += 1;
    } // if
    while ( !initialized ) {
      Thread.yield();
    } // while 
    paintLock.acquire();
    resetCoordinates();
    if ( marked ) {
      gc.setColor( Color.red );
    } else {
      gc.setColor( Color.black );
    } // if
    gc.drawString( getLabel(), textX, textY );
    gc.drawLine( x1, y1, x2, y2 );
    
    if ( Thread.currentThread().equals( t ) ) {	// let only the own thread paint packages to avoid deadlocks
      // paint packets
      for ( int i = 0; i < travellingPackets.size(); i += 1 ) {
	if ( node1.getPositionX() < node2.getPositionX() ) {
	  ( (Packet) travellingPackets.elementAt( i )).paint( gc, x1, y1, x2, y2 );
	} else {
	  ( (Packet) travellingPackets.elementAt( i )).paint( gc, x2, y2, x1, y1 );
	} // if
      } // for
    } // if
    paintLock.release();
    inPaint -= 1;
  } // LinkRIP.paint

  void resetCoordinates() {
    int x1, x2, y1, y2;
    while ( !initialized ) {
      try {
	wait( 100 );
      } catch ( InterruptedException e ) {
      } // try
    } // while
    x1 = node1.getPositionX();
    y1 = node1.getPositionY();
    x2 = node2.getPositionX();
    y2 = node2.getPositionY();
    nodeWidth = ((NetworkRIP)net).getNodeWidth();
    nodeHeight = ((NetworkRIP)net).getNodeHeight();
    if ( ( Math.min( y1, y2 ) + nodeHeight ) < Math.max( y1, y2 ) ) {
      this.x1 = Math.min( x1, x2 ) + ( nodeWidth / 2 );
      this.y1 = ( Math.min( x1, x2 ) == x1 ) ? y1 : y2;
      if ( this.y1 < Math.max( y1, y2 ) ) {
	this.y1 += nodeHeight;
      } // if
      this.x2 = Math.max( x1, x2 ) + ( nodeWidth / 2 );
      this.y2 = ( Math.max( x1, x2 ) == x2 ) ? y2 : y1;
      if ( this.y2 < Math.max( y1, y2 ) ) {
	this.y2 += nodeHeight;
      } // if
    } else {
      this.x1 = Math.min( x1, x2 ) + nodeWidth;
      this.x2 = Math.max( x1, x2 );
      this.y1 = ( Math.min( x1, x2 ) == x1 ) ? y1 : y2;
      this.y2 = ( Math.max( x1, x2 ) == x2 ) ? y2 : y1;
      this.y1 += ( nodeHeight / 2 );
      this.y2 += ( nodeHeight / 2 );
    } // if 
    textWidth = fontMetrics.stringWidth( getLabel() );
    textX = ( Math.max( this.x1, this.x2 ) - Math.min( this.x1, this.x2 ) ) / 2 + Math.min( this.x1, this.x2 ) + 1;
    textY = ( Math.max( this.y1, this.y2 ) - Math.min( this.y1, this.y2 ) ) / 2 + Math.min( this.y1, this.y2 ) - 1;
    xpos = Math.min( Math.min( this.x1, this.x2 ), textX );
    ypos = Math.min( Math.min( this.y1, this.y2 ), textY - textHeight );

    setHeight( Math.max( this.y1, this.y2 ) - Math.min( this.y1, this.y2 ) + textHeight );
    setWidth( Math.max( Math.max( this.x1, this.x2 ) - Math.min( this.x1, this.x2 ), textWidth ) );
    if ( width > textWidth ) {
      textX -= textWidth / 2;	// center label
    } // if
    absXPos = xpos;
    absYPos = ypos;
  } // LinkRIP.resetCoordinates    

  public void sendRoutingTableUpdate( NodeRIP source, Vector entries ) {
    if ( source.equals( node1 ) ) {
      addPacket( new Packet( true, Color.blue, "RoutingTableUpdate", (Object) entries,0, timestamp ) );
    } else {
      addPacket( new Packet( false, Color.blue, "RoutingTableUpdate", (Object) entries,0, timestamp ) );
    } // if
    timestamp += 5;		// leave some space to next packet
  } // LinkRIP.sendRoutingTableUpdate

  public void sendRoutingTableRequest( NodeRIP source ) {
    if ( source.equals( node1 ) ) {
      addPacket( new Packet( true, Color.orange, "RoutingTableRequest", null,0, timestamp ) );
    } else {
      addPacket( new Packet( false, Color.orange, "RoutingTableRequest", null,0, timestamp ) );
    } // if
    timestamp += 5;		// leave some space to next packet
  } // LinkRIP.sendRoutingTableRequest

  public void sendDataPacket( int sourceID, NodeRIP from, int toID ) {
    if ( from.equals( node1 ) ) {
      addPacket( new Packet( true, Color.magenta, "DataPacket", new Integer( sourceID ), timestamp, new Integer(toID) ) );
    } else {
      addPacket( new Packet( false, Color.magenta, "DataPacket", new Integer( sourceID ), timestamp, new Integer(toID) ) );
    } // if
  } // LinkRIP.sendDataPacket

  void processPacket( Packet p ) {
    if ( p.getContentName().equals( "RoutingTableUpdate" ) ) {
      Vector entry = ( Vector ) p.getMessage();
      if ( p.forwardDirection ) {
	node2.processRoutingTableUpdate( this, entry );
      } else {
	node1.processRoutingTableUpdate( this, entry );
      } // if
    } else if ( p.getContentName().equals( "RoutingTableRequest" ) ) {
      if ( p.forwardDirection ) {
	node2.sendRoutingEntries( this );
      } else {
	node1.sendRoutingEntries( this );
      } // if
    } else if ( p.getContentName().equals( "DataPacket" ) ) {
      if ( p.forwardDirection ) {
	node2.forwardPacket( ((Integer) p.getMessage()).intValue(), this, ((Integer)p.gettoID()).intValue() );
      } else {
	node1.forwardPacket( ((Integer) p.getMessage()).intValue(), this, ((Integer)p.gettoID()).intValue() );
      } // if
    } // if
  } // LinkRIP.processPacket

} // LinkRIP





